package msigraphx.graph;

import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.Vector;

import msigraphx.gibbsSampler.CondDistr;
import msigraphx.gibbsSampler.RandomVariable;
import msigraphx.gibbsSampler.RandomVariableValue;


public class NetworkVertex extends RandomVariable<String> {
	private List<NetworkVertex> parents;
	private List<NetworkVertex> children;
	private CondDistr<String> distr;
	private int vertexIndex; //must be unique in a network
	
	public NetworkVertex(int vertexIndex, String label) {
		super(label);
		this.parents = new Vector<NetworkVertex>();
		this.children = new Vector<NetworkVertex>();
		this.distr = new CondDistr<String>();
		this.vertexIndex = vertexIndex;
	}
	
	public NetworkVertex(int vertexIndex, String label,
			List<RandomVariableValue<String>> allowedValues,
			List<NetworkVertex> parents,
			CondDistr<String> distr) {
		super(label);
		this.vertexIndex = vertexIndex;
		this.allowedValues = allowedValues;
		this.value = new RandomVariableValue<String>(this);
		this.parents = new Vector<NetworkVertex>();
		this.children = new Vector<NetworkVertex>();
		for(NetworkVertex parent : parents) {
			this.addParent(parent);
		}
		this.distr = distr;
	}
	
	public void addParent(NetworkVertex parent) {
		parents.add(parent);
		if(!parent.isParent(this)) {
			parent.addChild(this);
		}
	}
	
	private void addChild(NetworkVertex child) {
		children.add(child);
		if(!child.isChild(this)) {
			throw new IllegalStateException("addChild() must be called only by addParent()");
		}
	}
	
	public boolean isParent(NetworkVertex vertex) {
		return children.contains(vertex);
	}
	
	public boolean isChild(NetworkVertex vertex) {
		return parents.contains(vertex);
	}
	
	public NetworkVertex getParentByVertexIndex(int vertexIndex) {
		for(NetworkVertex parent : parents) {
			if(parent.getVertexIndex() == vertexIndex) {
				return parent;
			}
		}
		return null;
	}
	
	public void removeParent(NetworkVertex vertex) {
		int parentIndex = parents.indexOf(vertex);
		if(parentIndex != -1) {
			parents.remove(parentIndex);
			distr.removeDependance(parentIndex);
			vertex.removeChild(this);
		}
	}
	
	private void removeChild(NetworkVertex vertex) {
		int childIndex = children.indexOf(vertex);
		if(childIndex != -1) {
			children.remove(childIndex);
			if(vertex.isChild(this)) {
				throw new IllegalStateException("removeChild() must be called only by removeParent()");
			}
		}
	}
	
	public void setCondDistrRow(RandomVariableValue<String> thisVal, Float prob, List<RandomVariableValue<String>> parentsVal) {
		distr.setDistrProb(thisVal, parentsVal, prob);
	}
	
	public void setCondDistrRow(RandomVariableValue<String> thisVal, Float prob, RandomVariableValue<String> ... parentsVal) {
		List<RandomVariableValue<String>> parentsValList = new Vector<RandomVariableValue<String>>();
		for(RandomVariableValue<String> randVarVal : parentsVal) {
			parentsValList.add(randVarVal);
		}
		distr.setDistrProb(thisVal, parentsValList, prob);
	}
	
	public void setValue(String newValue) {
		value.val = newValue;
	}
	
	public int getVertexIndex() {
		return vertexIndex;
	}
	
	public CondDistr<String> getCondDistr() {
		return distr;
	}

	public void setCondDistr(CondDistr<String> distr2) {
		distr = distr2;
	}
	

	public boolean equals(Object other) {
		if(!(other instanceof NetworkVertex)) {
			return false;
		}
		NetworkVertex otherRandVar = (NetworkVertex)other;
		if(!otherRandVar.getClass().equals(this.getClass())) {
			return false;
		}
		if(otherRandVar.getVertexIndex() != this.getVertexIndex()) {
			return false;
		}
		
		return true;
	}

	public List<NetworkVertex> getChildren() {
		return children;
	}
	
	public Set<NetworkVertex> getAllConnections() {
		Set<NetworkVertex> allConnections = new HashSet<NetworkVertex>();
		allConnections.addAll(this.children);
		allConnections.addAll(this.parents);
		return allConnections;
	}
	
	public boolean hasDescendant(Set<NetworkVertex> vertexes) {
		for(NetworkVertex child : children) {
			if(vertexes.contains(child)) {
				return true;
			}
			if(child.hasDescendant(vertexes)) {
				return true;
			}
		}
		return false;
	}
	
	public boolean checkConsistency() {
		return distr.checkCondDistr();
	}
}
